package com.wsoft.bpm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsoft.bpm.entity.ActNoticeEntity;
import com.wsoft.bpm.entity.ActProcessMappingEntity;
import com.wsoft.bpm.mapper.ActNoticeMapper;
import com.wsoft.bpm.service.IActNoticeService;
import com.wsoft.bpm.service.IActProcessMappingService;
import com.wsoft.bpm.service.RunProcessService;
import com.wsoft.bpm.vo.NoticeVO;
import com.wsoft.bpm.vo.TaskDoneVO;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.exception.Asserts;
import com.wsoft.core.utils.LoginUserUtil;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhonglj
 * @since 2024年11月14日 16:50:18
 */
@Service
public class ActNoticeServiceImpl extends ServiceImpl<ActNoticeMapper, ActNoticeEntity> implements IActNoticeService {

    @Resource
    private RuntimeService runtimeService;
    @Resource
    private RunProcessService runProcessService;

    @Override
    public List<ActNoticeEntity> unReadList() {
        Long loginUserId = LoginUserUtil.getUserInfo().getLoginUserVO().getId();
        return list(
                Wrappers.lambdaQuery(ActNoticeEntity.class)
                        .eq(ActNoticeEntity::getAssign, loginUserId.toString())
                        .eq(ActNoticeEntity::getIsRead, CommonConstant.YESNO_NO)
        );
    }

    @Override
    public void notice(String taskId, List<String> assigns) {
        if (CollUtil.isEmpty(assigns)) {
            Asserts.fail("[bpm.process.error.empty.notice.assigns]");
        }

        Task task = runProcessService.getAuthTask(taskId, true);
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();

        List<ActNoticeEntity> notices = assigns.stream().map(assign -> {
            ActNoticeEntity notice = new ActNoticeEntity();
            notice.setAssign(assign);
            notice.setIsRead(CommonConstant.YESNO_NO);
            notice.setTaskId(task.getId());
            notice.setTaskName(task.getName());
            notice.setProcessInstanceId(task.getProcessInstanceId());
            notice.setBusinessCode(processInstance.getBusinessKey());
            return notice;
        }).collect(Collectors.toList());

        saveBatch(notices);
    }

    @Override
    public void readNotice(Long id) {
        ActNoticeEntity entity = getById(id);
        if (entity == null) {
            Asserts.fail("[bpm.process.error.notice]");
        }

        entity.setIsRead(CommonConstant.YESNO_YES);
        updateById(entity);
    }

    @Resource
    private IActProcessMappingService actProcessMappingService;
    @Resource
    private HistoryService historyService;

    @Override
    public List<NoticeVO> packageExtraNoticeData(List<ActNoticeEntity> unReadList) {
        if (CollUtil.isNotEmpty(unReadList)) {
            List<HistoricProcessInstance> processInstances = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceIds(unReadList.stream().map(ActNoticeEntity::getProcessInstanceId).collect(Collectors.toSet()))
                    .list();
            Map<String, HistoricProcessInstance> processInstanceMap = processInstances.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, Function.identity()));
            Map<String, ActProcessMappingEntity> procInstToMappingMap = actProcessMappingService.list(
                            Wrappers.lambdaQuery(ActProcessMappingEntity.class)
                                    .in(ActProcessMappingEntity::getProcessInstantId, unReadList.stream().map(ActNoticeEntity::getProcessInstanceId).collect(Collectors.toList()))
                    ).stream()
                    .collect(Collectors.toMap(ActProcessMappingEntity::getProcessInstantId, Function.identity()));

            return unReadList.stream()
                    .filter(e -> {
                        ActProcessMappingEntity mapping = procInstToMappingMap.get(e.getProcessInstanceId());
                        return mapping != null && mapping.getDeleted() == CommonConstant.YESNO_NO;
                    })
                    .map(e -> {
                        NoticeVO convert = Convert.convert(NoticeVO.class, e);
                        // 设置默认标题流程名称+节点名称
                        convert.setTitle(Opt.ofBlankAble(processInstanceMap.get(e.getProcessInstanceId()))
                                .map(HistoricProcessInstance::getProcessDefinitionName)
                                .orElse("") + "-" + e.getTaskName());
                        convert.setBusinessKey(processInstanceMap.get(e.getProcessInstanceId()).getBusinessKey());
                        ActProcessMappingEntity mappingEntity = procInstToMappingMap.get(e.getProcessInstanceId());
                        if (mappingEntity != null) {
                            convert.setBusinessType(mappingEntity.getBusinessType());
                            convert.setBusinessValue(mappingEntity.getBusinessValue());
                            convert.setBusinessParam(mappingEntity.getBusinessParam());
                            // 配置自定义标题
                            if (StrUtil.isNotBlank(mappingEntity.getBusinessTitle())) {
                                convert.setTitle(mappingEntity.getBusinessTitle() + "-" + e.getTaskName());
                            }
                        }
                        return convert;
                    }).collect(Collectors.toList());
        }

        return CollUtil.newArrayList();
    }
}
